from http.client import HTTPResponse
import random
import uuid
from study.authentication import CsrfExemptSessionAuthentication
from django.views.decorators.csrf import csrf_exempt
from requests.models import requote_uri
from ability import timeutil
import traceback
from ability.enums import ErrorCode
from datetime import datetime, timedelta
import json
from re import template

from django.db import transaction
from ability.excepts import FormException, LogicException, WxPayException
from typing import Tuple

from django.http.response import HttpResponse, HttpResponseRedirect
from django.urls import reverse
from django.utils.http import base64, urlsafe_base64_decode
from django.contrib.auth.views import LoginView
from django.shortcuts import render
from rest_framework import permissions, viewsets
from rest_framework.generics import RetrieveAPIView
from rest_framework.views import APIView
from rest_framework.renderers import TemplateHTMLRenderer
from rest_framework.response import Response
from django.contrib.auth import authenticate, login as auth_login, logout as auth_logout
from ability.models import Chapter, Course, Exam, ExamQuestion, Experiment, Order, Paper, PaperQuestion, Question, Sms, Student, StudentCourse, Teacher, UserProfile, WxInfo, StudentExperiment
from ability.serializer.serializers import (ChapterListSerializer, ChapterSerializer, ExperimentSerializer, PaperSerializer, StuCourseListSerializer, CourseSerializer, ExamListSerializer, StuExamSerializer, StuExperimentListSerializer, StuExperimentSerializer,StudentCreateSerializer,StuExperimentChapterSerializer)
from ability.serializer.experiment import ExperimentListSerializer
import ability.permissions as pm
from django.conf import settings
from ability.format import CustomResponse, GetPostParaOrNone
import logging
from tencentcloud.common.exception.tencent_cloud_sdk_exception import TencentCloudSDKException
logger = logging.getLogger(__name__)
from weixin import Weixin, WeixinError, WeixinLogin
from study.wx import wxConfig, get_access_token
from rest_framework.authentication import SessionAuthentication, BasicAuthentication 
from study.parse import TextXMLParse
import base64
from dysms.demo_sms_send import send_sms
from study.utils import rand_str, del_login_cookie, set_login_cookie, get_success_url, banned_val, print_queries

from django_redis import get_redis_connection

from ability.models import ExperimentChapter
from ability.eanxing import *
import hashlib
import markdown
# Create your views here.

class LoggedViewMixin():
  def go_login(self, request):
    url = reverse('study:login') + '?prev_url=' 
    return HttpResponseRedirect(url)

class Index(APIView):
    """
    A view that returns a templated HTML representation of a given user.
    """
    renderer_classes = [TemplateHTMLRenderer]

    def get(self, request, *args, **kwargs):
      keyword = GetPostParaOrNone(self.request.GET, 'keyword')
      if keyword not in ['', None]:
        course_list = Course.objects.filter(course_name__contains=keyword).filter(status='N').order_by('-id').all()
        experiment_list = Experiment.objects.filter(experiment_name__contains=keyword).filter(status='N').order_by('-id').all()
        exam_list = Exam.objects.filter(exam_name__contains=keyword).filter(status='N').order_by('-id').all()
      else:
        course_list = Course.objects.filter(status='N').order_by('-id').all()[:2]
        experiment_list = Experiment.objects.filter(status='N').order_by('-id').all()[:2]
        exam_list = Exam.objects.filter(status='N').filter(status='N').order_by('-id').all()[:2]

      # 获取精品课列表
      course_list = StuCourseListSerializer(course_list, many=True, context={'request': self.request})
      # 获取精品实验列表
      experiment_list = ExperimentListSerializer(experiment_list, many=True)
      # get exam list
      exam_list = ExamListSerializer(exam_list, many=True)
      return Response({
        'user': self.request.user,
        'course_list': course_list.data,
        'experiment_list': experiment_list.data,
        'exam_list': exam_list.data
        }, template_name='index.html')

class Courses(APIView):
    """
    A view that returns a templated HTML representation of a given user.
    """
    renderer_classes = [TemplateHTMLRenderer]

    def get(self, request, *args, **kwargs):
      keyword = GetPostParaOrNone(self.request.GET, 'keyword')
      if keyword not in ['', None]:
        course_list = Course.objects.filter(course_name__contains=keyword, status='N').order_by('-id').all()
      else:
        course_list = Course.objects.filter(status='N').order_by('-id').all()

      # 获取精品课列表
      course_list = StuCourseListSerializer(course_list, many=True, context={'request': self.request})
      # 获取精品实验列表
      experiment_list = Experiment.objects.filter(status='N').order_by('-id').all()[:2]
      experiment_list = ExperimentListSerializer(experiment_list, many=True)
      # get exam list
      exam_list = Exam.objects.filter(status='N').order_by('-id').all()[:2]
      exam_list = ExamListSerializer(exam_list, many=True)
      return Response({
        'user': self.request.user,
        'course_list': course_list.data,
        'experiment_list': experiment_list.data,
        'exam_list': exam_list.data,
        'web_title': '精品课程'
        }, template_name='courses.html')

class Experiments(APIView):
    """
    A view that returns a templated HTML representation of a given user.
    """
    renderer_classes = [TemplateHTMLRenderer]

    def get(self, request, *args, **kwargs):
      keyword = GetPostParaOrNone(self.request.GET, 'keyword')
      if keyword not in ['', None]:
        experiment_list = Experiment.objects.filter(experiment_name__contains=keyword).filter(status='N').order_by('-id').all()
      else:
        experiment_list = Experiment.objects.filter(status='N').order_by('-id').all()

      # 获取精品课列表
      course_list = Course.objects.filter(status='N').order_by('-id').all()[:2]
      course_list = StuCourseListSerializer(course_list, many=True, context={'request': self.request})
      # 获取精品实验列表
      experiment_list = ExperimentListSerializer(experiment_list, many=True)
      # get exam list
      exam_list = Exam.objects.filter(status='N').order_by('-id').all()[:2]
      exam_list = ExamListSerializer(exam_list, many=True)
      return Response({
        'user': self.request.user,
        'course_list': course_list.data,
        'experiment_list': experiment_list.data,
        'exam_list': exam_list.data,
        'web_title': '实验'
        }, template_name='experiments.html')
    

class Exams(APIView):
    """
    A view that returns a templated HTML representation of a given user.
    """
    renderer_classes = [TemplateHTMLRenderer]

    def get(self, request, *args, **kwargs):
      keyword = GetPostParaOrNone(self.request.GET, 'keyword')
      if keyword not in ['', None]:
        exam_list = Exam.objects.filter(status='N').filter(exam_name__contains=keyword, status='N').order_by('-id').all()
      else:
        exam_list = Exam.objects.filter(status='N').filter(status='N').order_by('-id').all()

      # 获取精品课列表
      course_list = Course.objects.filter(status='N').order_by('-id').all()[:2]
      course_list = StuCourseListSerializer(course_list, many=True,  context={'request': self.request})
      # 获取精品实验列表
      experiment_list = Experiment.objects.filter(status='N').order_by('-id').all()[:2]
      experiment_list = ExperimentListSerializer(experiment_list, many=True)
      # get exam list
      exam_list = ExamListSerializer(exam_list, many=True)
      return Response({
        'user': self.request.user,
        'course_list': course_list.data,
        'experiment_list': experiment_list.data,
        'exam_list': exam_list.data,
        'web_title': '考试测评'
        }, template_name='exams.html')

class ExperimentViewTK(APIView):
  template_name = 'login.html'
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    try:
      pk = str(self.kwargs.get('pk'))
      tk = str(self.kwargs.get('tk'))

      # json_str = json.dumps({"user_phone": 15101065122, "user_id": 135, "user_tenant": 49, "task_id": 1234712554425, "experiment_id": 56, "courseware_id": 24})
      # get_redis_connection('eanxinguser').set(tk, json_str)

      # 用Tk去取用户信息 手机号码
      e_user_jsonstr = get_redis_connection('eanxinguser').get(tk)
      print("========")
      print(e_user_jsonstr)
      print("========")
      if e_user_jsonstr in ['', None]:
        raise FormException('获取用户信息失败,请手动登录系统!!!!')
      e_user = json.loads(json.loads(e_user_jsonstr))
      print(e_user)
      phone = e_user.get('user_phone')
      user_type = "S"
      em = ['', None]
      if (phone in em) or (pk in em) or (tk in em):
        raise FormException('参数不完整,请手动登录系统!!!!')
      user = UserProfile.get_by_phone(phone)
      if user is not None and user.user_status!='N':
        raise FormException('当前账号已被封禁，请联系管理员!!!!')
      print_queries()
      if user is None:
        with transaction.atomic():
          user = UserProfile.addOne(phone, phone, user_type, phone=phone)
          Student.addOne(user, studentID=phone)
      #把E安行的用户信息加到cookie里 task_id user_tenant 等信息
      user.e_user = e_user
      #url = get_success_url(user)
      response = HttpResponseRedirect("https://"+settings.APP_HOST + '/ttp/experiment/' + pk)
      print(settings.APP_HOST)
      print("重定向之后")
      del_login_cookie(response)
      set_login_cookie(response, user)
      auth_login(request, user)
      print("校验登录之后")
      return response
    except FormException as e:
      print(e)
      return Response({
        'teacher_login_page': settings.TEACHER_LOGIN_PAGE,
        'need_captcha': False,
        'error': str(e)
      }, template_name=self.template_name)

class Login(APIView):
  template_name = 'registration/mobile-login.html'
  renderer_classes = [TemplateHTMLRenderer]

  # 密码连续输错三次，需要验证码，同一手机号|同一ip短信验证码连续发送三次，需要验证码
  # def post(self, request, *args, **kwargs):
  #   try:
  #     phone = GetPostParaOrNone(self.request.data, 'phone')
  #     verifyCode = GetPostParaOrNone(self.request.data, 'verifyCode')
  #     user_type = GetPostParaOrNone(self.request.data, 'user_type', 'S')
  #     print('login params: ',phone, user_type)
  #     banned = ['', None]
  #     if phone in banned:
  #       raise FormException('手机号不可为空')
  #     if Sms.verify(phone, verifyCode) == False:
  #       raise FormException('短信验证码不正确')
  #     if user_type not in ['T', 'S']:
  #       user_type = 'S'
  #     user = UserProfile.get_by_phone(phone)
  #     print_queries()
  #     print('user', user)
  #     if user is None:
  #       with transaction.atomic():
  #         user = UserProfile.addOne(phone, phone, user_type, phone=phone)
  #         if user_type == 'S':
  #           Student.addOne(user, studentID=phone)
  #         else:
  #           Teacher.addOne(user, None, 'C')
  #     prev_url = GetPostParaOrNone(self.request.data, 'prev_url')
  #     print('prev_url', prev_url)
  #     if prev_url is not None:
  #       url = prev_url
  #     else:
  #       url = get_success_url(user)
  #     print('url', prev_url)
  #     response = HttpResponseRedirect(url)
  #     del_login_cookie(response)
  #     set_login_cookie(response, user)
  #     auth_login(request, user)
  #     return response
  #   except FormException as e:
  #     return Response({
  #       'teacher_login_page': settings.TEACHER_LOGIN_PAGE,
  #       'need_captcha': False,
  #       'error': str(e)
  #     }, template_name=self.template_name)

  def post(self, request, *args, **kwargs):
    try:
      phone = GetPostParaOrNone(self.request.data, 'phone')
      verifyCode = GetPostParaOrNone(self.request.data, 'verifyCode')
      banned = ['', None]
      if phone in banned:
        raise FormException('手机号不可为空')
      if verifyCode in banned:
        raise FormException('密码不能为空')
      user = UserProfile.get_by_phone(phone)
      print_queries()
      if user is None:
        raise FormException('用户不存在！！！')
      if user.user_type!='S' and user.user_type!= 'A':
        raise FormException('用户类型有误！！！')
      if user.user_status == 'L':
        raise FormException('账号已被封禁,请联系管理员/(ㄒoㄒ)/~~')
      if user.user_status == 'C':
        raise FormException('此账号已被注销/(ㄒoㄒ)/~~')
      prev_url = GetPostParaOrNone(self.request.data, 'prev_url')
      if hashlib.md5(verifyCode.encode()).hexdigest() != user.pwd:
          raise FormException('密码不正确')
      print('prev_url', prev_url)
      if prev_url is not None:
        url = prev_url
      else:
        url = settings.STU_LOGIN_REDIRECT_URL
      print('url', url)
      response = HttpResponseRedirect(url)
      del_login_cookie(response)
      set_login_cookie(response, user)
      auth_login(request, user)
      return response
    except FormException as e:
      return Response({
        'teacher_login_page': settings.TEACHER_LOGIN_PAGE,
        'need_captcha': False,
        'error': str(e)
      }, template_name="registration/mobile-login.html")
  # 原版登录都使用的同一个接口
  # def post(self, request, *args, **kwargs):
  #   try:
  #     phone = GetPostParaOrNone(self.request.data, 'phone')
  #     verifyCode = GetPostParaOrNone(self.request.data, 'verifyCode')
  #     banned = ['', None]
  #     if phone in banned:
  #       raise FormException('手机号不可为空')
  #     if verifyCode in banned:
  #       raise FormException('密码不能为空')
  #     user = UserProfile.get_by_phone(phone)
  #     print_queries()
  #     if user is None:
  #       raise FormException('管理员或者教师不存在,请联系管理员添加')
  #     if user.user_status == 'L':
  #       raise FormException('账号已被封禁,请联系管理员/(ㄒoㄒ)/~~')
  #     if user.user_status == 'C':
  #       raise FormException('此账号已被注销/(ㄒoㄒ)/~~')
  #     prev_url = GetPostParaOrNone(self.request.data, 'prev_url')
  #     if hashlib.md5(verifyCode.encode()).hexdigest() != user.pwd:
  #         raise FormException('密码不正确')
  #     print('prev_url', prev_url)
  #     if prev_url is not None:
  #       url = prev_url
  #     else:
  #       url = get_success_url(user)
  #     print('url', prev_url)
  #     response = HttpResponseRedirect(url)
  #     del_login_cookie(response)
  #     set_login_cookie(response, user)
  #     auth_login(request, user)
  #     return response
  #   except FormException as e:
  #     return Response({
  #       'teacher_login_page': settings.TEACHER_LOGIN_PAGE,
  #       'need_captcha': False,
  #       'error': str(e)
  #     }, template_name="registration/mobile-login.html")


  def get(self, request, *args, **kwargs):
    print(request.GET)
    prev_url = GetPostParaOrNone(request.GET, 'prev_url', reverse('study:index'))
    callback = settings.APP_HOST + reverse('study:wxLoginBack')
    state = str(uuid.uuid4())
    wx_login_url = 'https://open.weixin.qq.com/connect/qrconnect?appid=%s&redirect_uri=%s&response_type=code&scope=snsapi_login&state=%s&connect_redirect=1#wechat_redirect' % (settings.WX_LOGIN_APPID, callback, state)
    print(wx_login_url)
    request.session['state'] = state
    return Response({
      'teacher_login_page': settings.TEACHER_LOGIN_PAGE,
      'need_captcha': False,
      'prev_url': prev_url,
      'wx_login_url': wx_login_url
    }, template_name=self.template_name)
    
class WxLoginBack(APIView, LoggedViewMixin):

  queryset = WxInfo.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    code = GetPostParaOrNone(request.GET, 'code')
    state = GetPostParaOrNone(request.GET, 'state')
    prev_url = GetPostParaOrNone(request.GET, 'prev_url', reverse('study:index'))
    print('code:%s state: %s state from session: %s ' % (code, state, request.session.get('state')))
    print('sessions: ', request.session)
    if not code or not state:
      url = reverse('study:login') + '?prev_url=' + prev_url
      return HttpResponseRedirect(url)
    if request.session.get('state', False) != state:
      url = reverse('study:login') + '?prev_url=' + prev_url
      return HttpResponseRedirect(url)
    ret = get_access_token(code)
    if 'access_token' not in ret:
      url = reverse('study:login') + '?prev_url=' + prev_url
      return HttpResponseRedirect(url)

    wxinfo = WxInfo.getOne(ret['openid'])
    if wxinfo is None:
      wxinfo = WxInfo.create(ret['openid'], ret['unionid'])
      
    user = UserProfile.get_by_openid(ret['openid'])
    if user is None:
      # 跳转绑定手机页面
      request.session['openid'] = ret['openid']
      return Response({
      }, template_name='bind_phone.html')
    if user.openid is None:
      user.openid = ret['openid']
      user.save()

    prev_url = GetPostParaOrNone(self.request.data, 'prev_url')
    if prev_url is not None:
      url = prev_url
    else:
      url = get_success_url(user)
    response = HttpResponseRedirect(url)
    set_login_cookie(response, user)
    auth_login(request, user)
    return response

class BindPhone(APIView):

  queryset = WxInfo.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def post(self, request, *args, **kwargs):

    openid = request.session.get('openid')
    if openid is None:
      print('openid is none', openid)
      return HttpResponseRedirect(reverse('study:login'))
    
    wxinfo = WxInfo.getOne(openid)
    if wxinfo is None:
      print('wxinfo is none')
      return HttpResponseRedirect(reverse('study:login'))

    phone = GetPostParaOrNone(self.request.data, 'phone')
    verifyCode = GetPostParaOrNone(self.request.data, 'verifyCode')
    if phone in banned_val or verifyCode in banned_val:
      return HttpResponseRedirect(reverse('study:login'))

    try:
      # 该手机号已存在
      phoneUser = UserProfile.get_by_phone(phone)
      if phoneUser and phoneUser.openid not in banned_val and phoneUser.openid != openid:
        raise LogicException('该手机号已绑定过微信，请直接登录')

      # 该openid已存在
      openUser = UserProfile.get_by_openid(wxinfo.openid)
      if openUser and openUser.phone not in banned_val and phoneUser.phone != phone:
        raise LogicException('该微信已绑定过手机号，请直接登录')

      with transaction.atomic():
        if phoneUser is None:
          phoneUser = UserProfile.addOne(phone, phone, 'S', phone=phone)
          Student.addOne(phoneUser , studentID=phone)
        phoneUser.openid = openid
        phoneUser.save()
      prev_url = GetPostParaOrNone(self.request.data, 'prev_url')
      if prev_url is not None:
        url = prev_url
      else:
        url = get_success_url(phoneUser)
      response = HttpResponseRedirect(url)
      set_login_cookie(response, phoneUser)
      auth_login(request, phoneUser)
      return response
    except LogicException as e:
      return Response({
        'error': str(e)
      }, template_name='bind_phone.html')

class Logout(APIView, LoggedViewMixin):

  queryset = Experiment.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)

    response = HttpResponseRedirect(reverse('study:login'))
    del_login_cookie(response)
    return response 

class PwdLogin(APIView):

  renderer_classes = [TemplateHTMLRenderer]

  # 密码连续输错三次，需要验证码，同一手机号|同一ip短信验证码连续发送三次，需要验证码
  def post(self, request, *args, **kwargs):

    return Response({
      'need_captcha': False
      }, template_name='login.html')

  def get(self, request, *args, **kwargs):
    return Response({
      'need_captcha': False
      }, template_name='login.html')

class VerifyCode(APIView):

  def __init__(self, **kwargs):
    self.response_format = CustomResponse().response
    super(VerifyCode, self).__init__(**kwargs)

  # 同一手机号|同一ip短信验证码连续发送三次，需要验证码
  def post(self, request, *args, **kwargs):
    try:
      phone = GetPostParaOrNone(self.request.data, 'phone')
      if phone is None:
        raise LogicException('参数不正确')
      with transaction.atomic():
        sms = Sms.create(phone)
        params = {"code": sms.code}
        __business_id = uuid.uuid1()
        res = send_sms(__business_id, phone, "能力数据", "SMS_136735135", params)
        print(res)
        res = json.loads(res)
        print(res)
        if res['Code'] is None or res['Code'] != 'OK':
          print(res)
          raise LogicException('短信发送失败')
      self.response_format['message'] = '短信发送成功'
      return Response(self.response_format)
    except LogicException as e:
      self.response_format['error_code'] = -1
      self.response_format['message'] = str(e)
    except Exception as e:
      traceback.print_exc()
      self.response_format['error_code'] = -1
      self.response_format['message'] = '短信发送失败'
    return Response(self.response_format)

class CourseView(RetrieveAPIView):
  
  queryset = Course.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    self.object = self.get_object()
    course = CourseSerializer(self.object, context={'request': self.request})
    chapter = ChapterListSerializer(Chapter.get_by_course(course=self.object), many=True)
    return Response({
      'course': course.data,
      'chapters': chapter.data
      }, template_name='course/course.html')

class StudyingView(LoggedViewMixin, RetrieveAPIView):
  queryset = Course.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    # 如学生未购买该专业课，跳转到购买页
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
    self.object = self.get_object()
    course = CourseSerializer(self.object, context={'request': self.request})
    # todo does the student bought this course
    user = self.request.user
    if user.user_type == 'S':
      if StudentCourse.doesChoiced(user.student, self.object) == False:
        url = reverse('study:buyCourse', args=[self.object.id])
        return HttpResponseRedirect(url)
    # if user.user_type == 'T':


    chapter = ChapterListSerializer(Chapter.get_by_course(course=self.object), many=True)
    act_chapter = None
    act_chapter_model = None
    if 'chapter_id' in request.GET:
      chapter_id = request.GET['chapter_id']
      act_chapter_model = Chapter.get_by_id(chapter_id)
      act_chapter = ChapterSerializer(act_chapter_model)
    else:
      act_chapter_model = Chapter.objects.filter(course=self.object).first()
      act_chapter =  ChapterSerializer(act_chapter_model)
    if act_chapter_model is not None:
      act_chapter_model.pv += 1
      act_chapter_model.save()
    return Response({
      'course': course.data,
      'chapters': chapter.data,
      'act_chapter': act_chapter.data,
      'chapter': act_chapter.data
      }, template_name='course/studying.html') 

class CourseBuy(LoggedViewMixin, RetrieveAPIView):
  queryset = Course.objects.all()
  renderer_classes = [TemplateHTMLRenderer]
  template_name = 'pay.html'

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
    try:
      student = self.request.user.student
    except Exception as e:
      return Response({
        'error_msg': '请登录用户账号进行操作',
        'error_code': -1000
      }, template_name='403.html')
    try:
      self.object = self.get_object()
      order_no = Order.make_order_no()
      if Student.choicedCourse(student_id=student.id, course_id=self.object.id):
        url = reverse('study:studying', args=[self.object.id])
        return HttpResponseRedirect(url)

      body = settings.APP_NAME + '-课程购买'
      if self.object.course_type == 'E':
        # 如果是选修课，即免费课程
        with transaction.atomic():
          order = Order.addOne(order_no, body, self.request.user, self.object.price, self.object.id, Order.ORDER_TYPE_COURSE)
          student.course.add(self.object.id)
        url = reverse('study:studying', args=[self.object.id])
        return HttpResponseRedirect(url)

      if settings.APP_ENV == 'local':
        raw = dict(return_code='SUCCESS', code_url="sucess")
      else:
        weixin = Weixin(wxConfig)
        raw = weixin.unified_order(out_trade_no=order_no, body=body, total_fee=self.object.price, trade_type='NATIVE', product_id=str(self.object.id))
      if 'return_code' not in raw or raw['return_code'] != 'SUCCESS':
        raise LogicException('支付签名失败')
      with transaction.atomic():
        order = Order.addOne(order_no, body, self.request.user, self.object.price, self.object.id, Order.ORDER_TYPE_COURSE)
        
      return Response({
        'name': self.object.course_name,
        'price': self.object.price,
        'product_id': self.object.id,
        'raw': raw,
        'order_no': order_no
        }, template_name=self.template_name) 
    except LogicException as e:
      print(str(e))
      return Response({
        'error': str(e)
      }, template_name=self.template_name)
    except Exception as e:
      print(str(e))
      return Response({
        'error': '统一下单失败'
      }, template_name=self.template_name)

class ExperimentBuy(LoggedViewMixin, RetrieveAPIView):
  queryset = Experiment.objects.all()
  renderer_classes = [TemplateHTMLRenderer]
  template_name = 'pay.html'

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
    try:
      student = self.request.user.student
    except Exception as e:
      return Response({
        'error_msg': '请登录用户账号进行操作',
        'error_code': -1000
      }, template_name='403.html')
    try:
      self.object = self.get_object()
      url = reverse('study:experimenting', args=[self.object.id])
      if Student.choicedExperiment(student, self.object):
        return HttpResponseRedirect(url)

      order_no = Order.make_order_no()
      body = settings.APP_NAME + '-实验购买'

      # if the prudoct if free
      if self.object.price == 0:
        order = Order.addOne(order_no, body, self.request.user, self.object.price, self.object.id, Order.ORDER_TYPE_EXPERIMENT)
        self.object.student.add(student)
        return HttpResponseRedirect(url)
        
      if settings.APP_ENV == 'local':
        raw = dict(return_code='SUCCESS', code_url="sucess")
      else:
        weixin = Weixin(wxConfig)
        raw = weixin.unified_order(out_trade_no=order_no, body=body, total_fee=self.object.price, trade_type='NATIVE', product_id=str(self.object.id))
      if 'return_code' not in raw or raw['return_code'] != 'SUCCESS':
        raise LogicException('支付签名失败')
      with transaction.atomic():
        order = Order.addOne(order_no, body, self.request.user, self.object.price, self.object.id, Order.ORDER_TYPE_EXPERIMENT)

      return Response({
        'name': self.object.experiment_name,
        'price': self.object.price,
        'product_id': self.object.id,
        'raw': raw,
        'order_no': order_no
        }, template_name=self.template_name) 
    except LogicException as e:
      print(str(e))
      return Response({
        'error': str(e)
      }, template_name=self.template_name)
    except Exception as e:
      print(str(e))
      traceback.print_exc()
      return Response({
        'error': '统一下单失败'
      }, template_name=self.template_name)

class ExamBuy(LoggedViewMixin, RetrieveAPIView):
  queryset = Exam.objects.all()
  renderer_classes = [TemplateHTMLRenderer]
  template_name = 'pay.html'

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
    try:
      student = self.request.user.student
    except Exception as e:
      return Response({
        'error_msg': '请登录用户账号进行操作',
        'error_code': -1000
      }, template_name='403.html')
    try:
      self.object = self.get_object()
      order_no = Order.make_order_no()
      body = settings.APP_NAME + '-考试购买'

      url = reverse('study:examing', args=[self.object.id])
      if Student.choicedExperiment(student, self.object):
        return HttpResponseRedirect(url)

      print('price', self.object.price)
      # if the prudoct if free
      if self.object.price == 0:
        order = Order.addOne(order_no, body, self.request.user, self.object.price, self.object.id, Order.ORDER_TYPE_EXAM)
        self.object.student.add(student)
        return HttpResponseRedirect(url)

      if settings.APP_ENV == 'local':
        raw = dict(return_code='SUCCESS', code_url="sucess")
      else:
        weixin = Weixin(wxConfig)
        raw = weixin.unified_order(out_trade_no=order_no, body=body, total_fee=self.object.price, trade_type='NATIVE', product_id=str(self.object.id))
      if 'return_code' not in raw or raw['return_code'] != 'SUCCESS':
        raise LogicException('支付签名失败')
      with transaction.atomic():
        order = Order()
        order.order_no = order_no
        order.body = body
        order.user = self.request.user
        order.total_fee = self.object.price
        order.product_id = self.object.id
        order.order_type = Order.ORDER_TYPE_EXAM
        order.save()

      return Response({
        'name': self.object.exam_name,
        'price': self.object.price,
        'product_id': self.object.id,
        'raw': raw,
        'order_no': order_no
        }, template_name=self.template_name) 
    except LogicException as e:
      print(str(e))
      return Response({
        'error': str(e)
      }, template_name=self.template_name)
    except Exception as e:
      print(str(e))
      return Response({
        'error': '统一下单失败'
      }, template_name=self.template_name)
 
    
class PayNotify(APIView):

  parser_classes = [TextXMLParse]

  @csrf_exempt
  def post(self, request, *args, **kwargs):
    weixin = Weixin(wxConfig)
    try:
      data = self.request.data
      print(data)
      # 本地不环境无法验签
      if settings.APP_ENV != 'local' and not weixin.check(data):
        raise LogicException('签名验证失败')
      # 处理业务逻辑
      with transaction.atomic():
        order = Order.get_by_order_no(data['out_trade_no'])
        if order.status != 0:
          raise LogicException('重复通知')
        order.status = 1 if data['return_code'] == 'SUCCESS' else -1
        order.pay_at = datetime.now()
        order.save()
        if order.status == 1:
          student = order.user.student
          if order.order_type == Order.ORDER_TYPE_COURSE:
            student.course.add(order.product_id)
          if order.order_type == Order.ORDER_TYPE_EXPERIMENT:
            experiment = Experiment.get_by_pk(order.product_id)
            experiment.student.add(order.product_id)
          if order.order_type == Order.ORDER_TYPE_EXAM:
            exam = Exam.get_by_pk(order.product_id)
            exam.student.add(order.product_id)
      return Response(weixin.reply("OK", True))
    except LogicException as e:
      logger.error(str(e))
      return Response(weixin.reply("OK", True))
    except Exception as e:
      logger.error(str(e))
      traceback.print_exc()
      return Response(weixin.reply(str(e), False))

class PayQuery(APIView):

  permission_classes = [pm.CustomIsAuthenticated]
  authentication_classes = (CsrfExemptSessionAuthentication, BasicAuthentication)

  def __init__(self, **kwargs):
    self.response_format = CustomResponse().response
    self.message = ""
    super(PayQuery, self).__init__(**kwargs)

  def post(self, request, *args, **kwargs):
    try:
      print(self.request.user)
      order_no = GetPostParaOrNone(self.request.data, 'order_no')
      if order_no is None or len(order_no) != 20:
        raise LogicException('参数错误')
      order = Order.get_by_order_no(order_no)
      if order is None or order.user_id != self.request.user.id:
        raise LogicException('订单不存在')
      self.response_format['data'] = {
        'status': order.status,
        'jump_url': order.order_jump_url()
      }
      return Response(self.response_format)
    except LogicException as e:
      self.response_format['message'] = str(e)
      self.response_format['error_code'] = ErrorCode.comon_error.value 
      return Response(self.response_format)

class ExperimentView(LoggedViewMixin, RetrieveAPIView):
  
  queryset = Experiment.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  #判断是否有该实验，若无
  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
    error = None
    error_code = 0
    try:
      if self.request.user.user_type == 'A' and Student.objects.filter(user_id=self.request.user.id).count() == 0:
        student_new = Student()
        student_new.user = self.request.user
        student_new.save()
      student = self.request.user.student
    except Exception as e:
      return Response({
        'error_msg': '用户类型有误',
        'error_code': -1000
      }, template_name='403.html')
    try:
      if student is None:
        error_code = 1
        raise LogicException('请使用用户账号登录')

      # queryset = Experiment.objects.filter(student=student)
      experiment = self.queryset.get(id = self.kwargs['pk'])
      connect_course = None
      context={'request': self.request}
      if Experiment.objects.filter(id = experiment.id, student=student).count() == 0:
        stuExp = StudentExperiment(student=student, experiment=experiment)
        stuExp.vnccode = rand_str(8)
        stuExp.save()
        # error_code = 2
        # error = '请先报名参与该实验'
        # if experiment.type ==  Experiment.TYPE_COURSE:
        #   connect_course = StuCourseListSerializer(experiment.course.all(), many=True, context=context)

      stuExp = StudentExperiment.objects.filter(student=student, experiment=experiment).first() 
      if stuExp is None:
        stuExp = StudentExperiment(student=student, experiment=experiment)
        stuExp.vnccode = rand_str(8)
        stuExp.save()
        print("学生==实验保存成功")

      # todo experiment expired
      # if timeutil.comp(experiment.ending_time, datetime.now()) == -1:
      #   error_code = 3
      #   error = '实验已过截止时间'
      serializer = StuExperimentSerializer(experiment, context={"student": request.user.student})
      # print(serializer.data)
      course_list = Course.objects.order_by('-id').all()[:2]
      experiment_list = Experiment.objects.order_by('-id').all()[:2]
      exam_list = Exam.objects.order_by('-id').all()[:2]
      # 获取精品课列表
      course_list = StuCourseListSerializer(course_list, many=True, context = context)
      # 获取精品实验列表
      experiment_list = ExperimentListSerializer(experiment_list, many=True)
      # get exam list
      exam_list = ExamListSerializer(exam_list, many=True)

      describe_list = ExperimentChapter.objects.filter(experiment_id=experiment.id).values('detail', 'pv')
      aa = []
      for u in describe_list:
        detail = markdown.markdown(u['detail'], extensions=['markdown.extensions.fenced_code']).strip()
        title_flag = '</p>'
        if detail.find("<h1>") == 0:
          title_flag = "</h1>"
        if detail.find("<h3>") == 0:
          title_flag = "</h3>"
        if detail.find("<h2>") == 0:
          title_flag = "</h2>"
        aa.append({
          "detail": detail,
          "title": detail[len(f"{title_flag}")-1:detail.find(f"{title_flag}"):1],
          'pv': u['pv']
        })
      indexed_list = [(index, item) for index, item in enumerate(aa)]
      experiment_data = serializer.data
      experiment_data['intro'] = markdown.markdown(experiment_data['intro'], extensions=['markdown.extensions.fenced_code'])
      experiment_data['describe'] = markdown.markdown(experiment_data['describe'], extensions=['markdown.extensions.fenced_code'])
      return Response({
        'describe_list': indexed_list,
        'experiment': experiment_data,
        'vnccode': stuExp.vnccode,
        'course_list': course_list.data,
        'experiment_list': experiment_list.data,
        'exam_list': exam_list.data,
        'connect_course': connect_course.data if connect_course != None else [],
        'error_code': error_code,
        'error': error,
        'user': request.user
      }, template_name='experiment/experiment.html')
    except Experiment.DoesNotExist:
      return Response(template_name='404.html')
    except TencentCloudSDKException as e:
      error = '实例状态获取失败'
    except LogicException as e:
      error = str(e)
    except Exception as e:
      traceback.print_exc()
      error = '实例请求失败'
    
    return Response({
      'experiment': 'none',
      'error_msg': error,
      'error_code': -2
    }, template_name='experiment/experiment.html')


#新增根据实验id和学练手册步骤id获取学练手册信息
class StuExperimentChapterView(LoggedViewMixin, RetrieveAPIView):
  def get(self, request, *args, **kwargs):
      # experiment = self.queryset.get(id=self.kwargs['pk'])
      experiment_chapter_list = ExperimentChapter.objects.filter(experiment_id=self.kwargs['pk']).order_by('id')
      if self.kwargs['experiment_chapter_id']<0:
        serializer = StuExperimentChapterSerializer(experiment_chapter_list[0])
        # raise LogicException('当前为第一步')
      elif self.kwargs['experiment_chapter_id']>=len(experiment_chapter_list):
        serializer = StuExperimentChapterSerializer(experiment_chapter_list[len(experiment_chapter_list)-1])
        # raise LogicException('当前为最后一步')
      else:
        serializer = StuExperimentChapterSerializer(experiment_chapter_list[self.kwargs['experiment_chapter_id']])
      experiment_chapter_data = serializer.data
      return Response({
        'experiment_chapter': experiment_chapter_data,
        'error_code': 0,
        'code': 200
      }, template_name='experiment/experiment.html')

class ReportView(RetrieveAPIView):
  
  queryset = Experiment.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  #判断是否有该实验，若无
  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
    try:
      student = self.request.user.student
      error = None
      error_code = 0
      if student is None:
        error_code = 1
        raise LogicException('请使用用户账号登录')

      # experiment = self.queryset.get(id = self.kwargs['pk'])
      self.object = self.get_object()
      context={'request': self.request}
      if Experiment.objects.filter(id = self.object.id, student=student).count() == 0:
        print('not buy')
        return HttpResponseRedirect(reverse('study:experiment', args=[self.object.id]))

      # todo experiment expired
      # if timeutil.comp(self.object.ending_time, datetime.now()) == -1:
      #   return HttpResponseRedirect(reverse('study:experiment', args=[self.object.id]))
      serializer = ExperimentSerializer(self.object, context=context)
      print(serializer.data)
      return Response({
        'experiment': serializer.data,
        'error_code': error_code,
        'error': error
      }, template_name='experiment/report.html')
    except Experiment.DoesNotExist:
      return Response(template_name='404.html')
    except TencentCloudSDKException as e:
      error = '实例状态获取失败'
    except LogicException as e:
      error = str(e)
    except Exception as e:
      traceback.print_exc()
      error = '实例请求失败'
    return Response({
      'error_msg': error,
      'error_code': error_code
    }, template_name='experiment/report.html')

class ExamView(LoggedViewMixin, RetrieveAPIView):

  queryset = Exam.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    # todo 兼容不定时考试。
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
    self.object = self.get_object()
    serializer = StuExamSerializer(self.object, context={'request': self.request})
    if self.object.start_time is None:
      # 不定时
      paper = Paper.get_one(self.request.user.student, self.object)
      submited = paper != None and paper.status == 'C'
      started = None
      expired = None
      start_ts = 0
      if paper:
        started = True
        expired = timeutil.expired(paper.expire_time)
        start_ts = int(paper.create_time.timestamp())
        print(paper.expire_time, expired, start_ts)

    else:
      # 定时考试取试卷过期时间等
      expire_time = timedelta(minutes=self.object.duration) + self.object.start_time
      paper = Paper.get_one(self.request.user.student, self.object)
      submited = paper != None and paper.status == 'C'
      started = timeutil.expired(self.object.start_time)
      expired = timeutil.expired(expire_time)
      start_ts = int(self.object.start_time.timestamp())
    
    return Response({
      'exam': serializer.data,
      'choiced': Student.choicedExam(self.request.user.student, self.object),
      'start_ts': start_ts,
      'started': started,
      'expired': expired,
      'submited': submited
    }, template_name='exam/exam.html')
 
class PaperView(RetrieveAPIView, LoggedViewMixin):

  queryset = Paper.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
    student = self.request.user.student
    error = None
    error_code = 0
    if student is None:
      error_code = 1
      raise LogicException('请使用用户账号登录')
    paper = Paper.objects.filter(exam_id=self.kwargs['exam_id'], student=student).first() 
    serializer = PaperSerializer(paper, context={"request": request})
    data = serializer.data

    mc_score = 0
    for mc in data['mc']:
      mc_score += mc['correctscore']
    ma_score = 0
    for ma in data['ma']:
      ma_score += ma['correctscore']
    tf_score = 0
    for tf in data['tf']:
      tf_score += tf['correctscore']
    bl_score = 0
    for bl in data['bl']:
      bl_score += bl['correctscore']
    sa_score = 0
    for sa in data['sa']:
      sa_score += sa['correctscore']

    print(json.dumps(serializer.data))
    return Response({
      'paper': data,
      'error_code': error_code,
      'mc_score': mc_score,
      'ma_score': ma_score,
      'tf_score': tf_score,
      'bl_score': bl_score,
      'sa_score': sa_score,
    }, template_name='exam/paper.html')

class ExamingView(LoggedViewMixin, RetrieveAPIView):

  queryset = Exam.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
    try:
      # 1、判断是否有权限
      print(self.kwargs)
      self.object = self.get_object()
      student = self.request.user.student
      pk = self.kwargs.get('pk')
      print(pk, self.object, self.object.start_time)
      if not Student.choicedExam(student, self.object):
        print('not choiced')
        return HttpResponseRedirect( reverse('study:exam', args=[pk]))
      # 1.5、不定时考试或定时考试？
      if self.object.start_time is None:
        # 不定时考试
        with transaction.atomic():
          # 取出试卷
          paper = Paper.get_one(student, self.object)
          if paper is None:
            # create paper 
            paper = Paper.addOne(student, self.object)
            knowledges = list(self.object.knowledge.all().values_list('id', flat=True))
            print('knowledges', knowledges)
            mc_ids = list(Question.objects.filter(knowledge__id__in=knowledges, question_type='MC').values_list('id', flat=True))
            ma_ids = list(Question.objects.filter(knowledge__id__in=knowledges, question_type='MA').values_list('id', flat=True))
            tf_ids = list(Question.objects.filter(knowledge__id__in=knowledges, question_type='TF').values_list('id', flat=True))
            bl_ids = list(Question.objects.filter(knowledge__id__in=knowledges, question_type='BL').values_list('id', flat=True))
            sa_ids = list(Question.objects.filter(knowledge__id__in=knowledges, question_type='SA').values_list('id', flat=True))
            # 获取单选题
            print('mc_ids: ', mc_ids)
            rand_ids = random.sample(mc_ids, int(self.object.mc_total))
            mc_questions = Question.objects.filter(id__in=rand_ids)

            # 获取多选题
            rand_ids = random.sample(ma_ids, int(self.object.ma_total))
            ma_questions = Question.objects.filter(id__in=rand_ids)
            
            # 获取判断题
            rand_ids = random.sample(tf_ids, int(self.object.tf_total))
            tf_questions = Question.objects.filter(id__in=rand_ids)

            # 获取填空题
            rand_ids = random.sample(bl_ids, int(self.object.bl_total))
            bl_questions = Question.objects.filter(id__in=rand_ids)

            # 获取简答题
            rand_ids = random.sample(sa_ids, int(self.object.sa_total))
            sa_questions = Question.objects.filter(id__in=rand_ids)
            
            mc_single_score = int(self.object.mc_score) / int(self.object.mc_total)
            ma_single_score = int(self.object.ma_score) / int(self.object.ma_total)
            tf_single_score = int(self.object.tf_score) / int(self.object.tf_total)
            bl_single_score = int(self.object.bl_score) / int(self.object.bl_total)
            sa_single_score = int(self.object.sa_score) / int(self.object.sa_total)

            order = 0
            for mcq in mc_questions:
                order = order + 1
                eq = ExamQuestion.addOne(self.object, order, mcq, mc_single_score)
                PaperQuestion.addOne(paper, eq)
            for maq in ma_questions:
                order = order + 1
                eq = ExamQuestion.addOne(self.object, order, maq, ma_single_score)
                PaperQuestion.addOne(paper, eq)
            for tfq in tf_questions:
                order = order + 1
                eq = ExamQuestion.addOne(self.object, order, tfq, tf_single_score)
                PaperQuestion.addOne(paper, eq)
            for blq in bl_questions:
                order = order + 1
                eq = ExamQuestion.addOne(self.object, order, blq, bl_single_score)
                PaperQuestion.addOne(paper, eq)
            for saq in sa_questions:
                order = order + 1
                eq = ExamQuestion.addOne(self.object, order, saq, sa_single_score)
                PaperQuestion.addOne(paper, eq)
      else:
        # 定时考试
        # 2、判断是否开始、是否结束
        if timeutil.comp(self.object.start_time, datetime.now()) == 1:
          print('not started', self.object.start_time)
          return HttpResponseRedirect( reverse('study:exam', args=[pk]))
        if timeutil.expired(self.object.start_time + timedelta(minutes=self.object.duration)): 
          print('expired', self.object.start_time)
          return HttpResponseRedirect( reverse('study:exam', args=[pk]))

        # 3、是否为首次进入，是：取出答案，与已提交答题；否：创建试卷 paper
        with transaction.atomic():
          paper = Paper.get_one(student, self.object)
          if paper is None:
            # create paper 
            paper = Paper.addOne(student, self.object)
            # create paper question
            for examques in ExamQuestion.objects.filter(exam=self.object).all():
              pq = PaperQuestion.addOne(paper, examques)

      serializer = PaperSerializer(paper, context={'request': self.request})
      print(json.dumps(serializer.data))
      return Response({
        'paper': serializer.data
      }, template_name='exam/examing.html')
    except LogicException as e:
      error = str(e)
    except Exception as e:
      traceback.print_exc()
      error = str(e)
    return Response({
      'error': error 
    }, template_name='exam/examing.html')
    
 
class UserCenter(APIView, LoggedViewMixin):

  queryset = Experiment.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
    return Response({
      'user': self.request.user
    }, template_name='user/user.html')

class UserCourse(APIView, LoggedViewMixin):

  queryset = Experiment.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
    
    # 如果当前登录非学生账户
    if self.request.user.user_type != 'S':
      return HttpResponseRedirect(reverse('logout'))

    courses = Course.objects.filter(student=self.request.user.student).all()
    serializer = StuCourseListSerializer(courses, many=True)
    print(json.dumps(serializer.data))
    return Response({
      'courses': serializer.data
    }, template_name='user/courses.html')

class UserExperiment(APIView, LoggedViewMixin):

  queryset = Experiment.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)  

    # 如果当前登录非学生账户
    if self.request.user.user_type != 'S':
      return HttpResponseRedirect(reverse('logout'))

    experiment = Experiment.objects.filter(student=self.request.user.student).all()
    serializer = StuExperimentListSerializer(experiment, many=True, context={'request', self.request})
    print(json.dumps(serializer.data))

    return Response({
      'experiments': serializer.data
    }, template_name='user/experiments.html')

class UserExam(APIView, LoggedViewMixin):

  queryset = Experiment.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)
     

    return Response({

    }, template_name='user/exams.html')

class UserKonledges(APIView, LoggedViewMixin):

  queryset = Experiment.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)

    return Response({

    }, template_name='user/knowledges.html')

class InsVNCView(APIView, LoggedViewMixin):

  queryset = Experiment.objects.all()
  renderer_classes = [TemplateHTMLRenderer]

  def get(self, request, *args, **kwargs):
    if self.request.user.is_authenticated == False:
      return self.go_login(request)

    return Response({
      "vnc_url": str(base64.b64decode(request.GET['vnc_url']).decode('utf-8'))
    }, template_name='vnc.html')

class Download(APIView):
  def get(self, request, *args, **kwargs):
    zip_file = open('/data/achive/nengli.zip', 'rb')
    response = HttpResponse(zip_file, content_type='application/force-download')
    response['Content-Disposition'] = 'attachment; filename="%s"' % 'nengli.zip'
    return response

